﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OWLServer.owl.tipos;
using System.Xml.Linq;
using OWLServer.owl.etiquetas;

namespace OWLServer.owl.utilidades
{
    /// <summary>
    /// Clase que contiene métodos para manejar las propiedades de una ontología.
    /// </summary>
    class UtilidadesPropiedades
    {
         /// <summary>
        /// Construye las propiedades del OWL
        /// </summary>
        /// <param name="ontologia">Ontología</param>
        /// <param name="root">Raiz del documento OWL</param>
        public static void construirPropiedades(Ontologia ontologia, XElement root)
        {
            // Insertamos las propiedades especiales
            crearPropiedadesEspeciales(ontologia, ontologia.getEspaciosNombresDictiorary());

            // Contruimos las propiedades
            construirPropiedadesDatos(ontologia, root);
            construirPropiedadesObjetos(ontologia, root);

            // Extraemos los grupos de propiedades disjuntas
            estraerGruposPropiedadesDisjuntas(ontologia, root);
        }

        /// <summary>
        /// Construye las propiedades de los datos del OWL
        /// </summary>
        /// <param name="ontologia">Ontología</param>
        /// <param name="root">Raiz del documento OWL</param>
        private static void construirPropiedadesDatos(Ontologia ontologia, XElement root)
        {
            if (UtilidadesEspaciosNombres.contieneOWL(ontologia.getEspaciosNombresDictiorary()) && UtilidadesEspaciosNombres.contieneRDF(ontologia.getEspaciosNombresDictiorary()) && UtilidadesEspaciosNombres.contieneRDFS(ontologia.getEspaciosNombresDictiorary()) && UtilidadesEspaciosNombres.contieneXSD(ontologia.getEspaciosNombresDictiorary()))
            {
                Elemento elemento;
                Propiedad propiedad;
                Elemento about;

                // Obtenemos las listas de propiedades que vamos a manejar
                IList<Propiedad> propiedades = ontologia.PropiedadesDatos;

                // Extraemos los elementos de las propiedades
                IEnumerable<XElement> elePropiedades =
                    from el in root.Elements(UtilidadesEspaciosNombres.getEspacioNombres(ontologia.getEspaciosNombresDictiorary()[EtiquetasOWL.NOMBRE]) + EtiquetasOWL.PROPIEDAD_DATOS)
                    select el;

                // Creamos los objetos Clase a partir de los elementos recuperados
                foreach (XElement ele in elePropiedades)
                {
                    elemento = UtilidadesElementos.getElemento(UtilidadesElementos.getId(ele, ontologia.getEspaciosNombresDictiorary()), ontologia);
                    propiedad = new Propiedad(elemento);

                    // Extraemos los dominios
                    extraerDominios(propiedad, ontologia, ele);

                    // Extraemos los rangos
                    extraerRangos(propiedad, ontologia, ele);

                    // Extraemos las propiedades
                    extraerPropiedades(propiedad, ontologia, ele);

                    // La relacionesmos con la propiedad top
                    relacionarPropiedadConPropiedadDatoTop(ontologia, ontologia.getEspaciosNombresDictiorary(), propiedad);
                    
                    propiedades.Add(propiedad);
                }

                // Extraemos los elementos de las propiedades que tengan subpropiedades, equivalentes o disjuntas
                elePropiedades =
                    from el in root.Elements(UtilidadesEspaciosNombres.getEspacioNombres(ontologia.getEspaciosNombresDictiorary()[EtiquetasOWL.NOMBRE]) + EtiquetasOWL.PROPIEDAD_DATOS)
                    where el.Elements(UtilidadesEspaciosNombres.getEspacioNombres(ontologia.getEspaciosNombresDictiorary()[EtiquetasRDFS.NOMBRE]) + EtiquetasRDFS.SUBPROPIEDAD).Count() > 0
                        || el.Elements(UtilidadesEspaciosNombres.getEspacioNombres(ontologia.getEspaciosNombresDictiorary()[EtiquetasOWL.NOMBRE]) + EtiquetasOWL.PROPIEDAD_EQUIVALENTE).Count() > 0
                        || el.Elements(UtilidadesEspaciosNombres.getEspacioNombres(ontologia.getEspaciosNombresDictiorary()[EtiquetasOWL.NOMBRE]) + EtiquetasOWL.DISJUNTA_CON_PROPIEDAD).Count() > 0
                    select el;

                foreach (XElement elementoPropiedad in elePropiedades)
                {
                    about = UtilidadesElementos.getElemento(UtilidadesElementos.getId(elementoPropiedad, ontologia.getEspaciosNombresDictiorary()), ontologia);

                    // Comprobamos si son sub propiedades de alguna propiedad
                    extraerSupPropiedades(about, ontologia, elementoPropiedad, true);

                    // Comprobamos si es equivalente a alguna propiedad con otra
                    extraerEquivalentesPropiedades(about, ontologia, elementoPropiedad, true);

                    // Comprobamos si es disjunta a alguna propiedad con otra
                    extraerDisjuntasPropiedades(about, ontologia, elementoPropiedad, true);
                }
            }
        }

        /// <summary>
        /// Extrae si son subpropiedades de alguna propiedad
        /// </summary>
        /// <param name="about">Elemento que hemos identificado</param>
        /// <param name="ontologia">Ontolología a la que pertenece</param>
        /// <param name="dicPropiedades">Diccionario de propiedades</param>
        /// <param name="elementoPropiedad">Elemento que representa a la propiedad que estamos analizando</param>
        /// <param name="isPropiedadDato">Indica si es una propiedad Dato o de Valor</param>
        private static void extraerSupPropiedades(Elemento about, Ontologia ontologia, XElement elementoPropiedad, bool isPropiedadDato)
        {
            Elemento recurso;
            Propiedad propiedadAbout = getPropiedad(ontologia, about, isPropiedadDato);
            Propiedad propiedadRecurso = null;

            IEnumerable<XElement> eleSubPropiedad =
                        from el in elementoPropiedad.Elements(UtilidadesEspaciosNombres.getEspacioNombres(ontologia.getEspaciosNombresDictiorary()[EtiquetasRDFS.NOMBRE]) + EtiquetasRDFS.SUBPROPIEDAD)
                        select el;

            foreach (XElement eleSub in eleSubPropiedad)
            {
                recurso = UtilidadesElementos.getElemento(UtilidadesElementos.getNombreRecurso(eleSub, ontologia.getEspaciosNombresDictiorary()), ontologia);
                propiedadRecurso = getPropiedad(ontologia, about, isPropiedadDato);
                propiedadAbout.PadrePropiedades.Add(propiedadRecurso);
                propiedadRecurso.SubPropiedades.Add(propiedadAbout);
            }
        }

        /// <summary>
        /// Extrae si es equivalente a alguna propiedad con otra
        /// </summary>
        /// <param name="about">Elemento que hemos identificado</param>
        /// <param name="ontologia">Ontolología a la que pertenece</param>
        /// <param name="dicPropiedades">Diccionario de propiedades</param>
        /// <param name="elementoPropiedad">Elemento que representa a la propiedad que estamos analizando</param>
        /// <param name="isPropiedadDato">indica si la propiedad es de tipo Dato o de Objeto</param>
        private static void extraerEquivalentesPropiedades(Elemento about, Ontologia ontologia, XElement elementoPropiedad, bool isPropiedadDato)
        {
            Elemento recurso;
            Propiedad propiedadAbout = getPropiedad(ontologia, about, isPropiedadDato);
            Propiedad propiedadRecurso = null;

            IEnumerable<XElement> eleEquiPropiedad =
                        from el in elementoPropiedad.Elements(UtilidadesEspaciosNombres.getEspacioNombres(ontologia.getEspaciosNombresDictiorary()[EtiquetasOWL.NOMBRE]) + EtiquetasOWL.PROPIEDAD_EQUIVALENTE)
                        select el;

            foreach (XElement eleEqv in eleEquiPropiedad)
            {
                recurso = UtilidadesElementos.getElemento(UtilidadesElementos.getNombreRecurso(eleEqv, ontologia.getEspaciosNombresDictiorary()), ontologia);
                propiedadRecurso = getPropiedad(ontologia, recurso, isPropiedadDato);
                propiedadAbout.EquivalentesPropiedades.Add(propiedadRecurso);
                propiedadRecurso.EquivalentesPropiedades.Add(propiedadAbout);
            }
        }

        /// <summary>
        /// Extrae si es disjunta a alguna propiedad con otra
        /// </summary>
        /// <param name="about">Elemento que hemos identificado</param>
        /// <param name="ontologia">Ontolología a la que pertenece</param>
        /// <param name="dicPropiedades">Diccionario de propiedades</param>
        /// <param name="elementoPropiedad">Elemento que representa a la propiedad que estamos analizando</param>
        /// <param name="isPropiedadDato">Indica si la propiedad es de dato o es de objeto</param>
        private static void extraerDisjuntasPropiedades(Elemento about, Ontologia ontologia, XElement elementoPropiedad, bool isPropiedadDato)
        {
            Elemento recurso;
            Propiedad propiedadAbout = getPropiedad(ontologia, about, isPropiedadDato);
            Propiedad propiedadRecurso = null;

            IEnumerable<XElement> eleDisjPropiedad =
                        from el in elementoPropiedad.Elements(UtilidadesEspaciosNombres.getEspacioNombres(ontologia.getEspaciosNombresDictiorary()[EtiquetasOWL.NOMBRE]) + EtiquetasOWL.DISJUNTA_CON_PROPIEDAD)
                        select el;

            foreach (XElement eleDisj in eleDisjPropiedad)
            {
                recurso = UtilidadesElementos.getElemento(UtilidadesElementos.getNombreRecurso(eleDisj, ontologia.getEspaciosNombresDictiorary()), ontologia);
                propiedadRecurso = getPropiedad(ontologia, recurso, isPropiedadDato); ;
                propiedadAbout.DisjuntasPropiedades.Add(propiedadRecurso);
                propiedadRecurso.DisjuntasPropiedades.Add(propiedadAbout);
            }
        }

        /// <summary>
        /// Extrae los grupos de propiedades disjuntas
        /// </summary>
        /// <param name="ontologia">Ontolología con la que estamos tratando</param>
        /// <param name="root">Documento OWL de la ontología</param>
        private static void estraerGruposPropiedadesDisjuntas(Ontologia ontologia, XElement root)
        {
            bool hayDisjuntos = false;
            IEnumerable<XElement> eleDisjuntos;
            IEnumerable<XElement> eleDisjunto;
            List<Propiedad> listaDisjuntos;
            Propiedad propiedad;
            Elemento elemento;
            int numeroPropiedadesDato;
            int numeroPropiedadesObjeto;
            List<Elemento> propiedadesDesconocidas;

            // Recorremos als descripciones que hay
            IEnumerable<XElement> eleDescripciones =
                from el in root.Elements(UtilidadesEspaciosNombres.getEspacioNombres(ontologia.getEspaciosNombresDictiorary()[EtiquetasRDF.NOMBRE]) + EtiquetasRDF.DESCRIPCION)
                select el;

            // Recorremos las Descripciones
            foreach (XElement eleDescripcion in eleDescripciones)
            {
                hayDisjuntos = UtilidadesElementos.isElementoDescripcionTipo(eleDescripcion, EtiquetasOWL.TODAS_PROPIEDADES_DISJUNTAS, ontologia);

                // Si es de tipo disjunto extraemos la coleccion
                if (hayDisjuntos)
                {
                    propiedadesDesconocidas = new List<Elemento>();
                    numeroPropiedadesDato = 0;
                    numeroPropiedadesObjeto = 0;

                    eleDisjuntos =
                    from el in eleDescripcion.Elements(UtilidadesEspaciosNombres.getEspacioNombres(ontologia.getEspaciosNombresDictiorary()[EtiquetasOWL.NOMBRE]) + EtiquetasOWL.MIEMBROS)
                    select el;

                    /// Suponemos que es una colección por eso no se comprueba el atributo rdf:parseType="Collection"

                    eleDisjunto =
                    from el in eleDisjuntos.Elements(UtilidadesEspaciosNombres.getEspacioNombres(ontologia.getEspaciosNombresDictiorary()[EtiquetasRDF.NOMBRE]) + EtiquetasRDF.DESCRIPCION)
                    select el;

                    listaDisjuntos = new List<Propiedad>();
                    // Obtenemos la lista de elementos que son distintos unos con otros
                    foreach (XElement eleDist in eleDisjunto)
                    {
                        elemento = UtilidadesElementos.getElemento(UtilidadesElementos.getId(eleDist, ontologia.getEspaciosNombresDictiorary()), ontologia);

                        propiedad = null;

                        if (ontologia.getPropiedadesObjetosDictiorary().ContainsKey(elemento.ToString()))
                        {
                            propiedad = ontologia.getPropiedadesObjetosDictiorary()[elemento.ToString()];
                            numeroPropiedadesObjeto++;
                        }
                        else if (ontologia.getPropiedadesDatosDictiorary().ContainsKey(elemento.ToString()))
                        {
                            propiedad = ontologia.getPropiedadesDatosDictiorary()[elemento.ToString()];
                            numeroPropiedadesDato++;
                        }
                        else // Las que no esten añadidas se agregaran luego
                        {
                            propiedadesDesconocidas.Add(elemento);
                        }

                        if (null != propiedad)
                        {
                            listaDisjuntos.Add(propiedad);
                        }
                    }

                    // Miramos las que nos hemos reconocido para agregarlas
                    // Todas las propiedades que se agregan a la coleccion son del mismo tipo (Dato o Objeto)
                    if (numeroPropiedadesDato > numeroPropiedadesObjeto)
                    {
                        foreach (Elemento propDesc in propiedadesDesconocidas)
                        {
                            listaDisjuntos.Add(getPropiedadDato(ontologia, propDesc));
                        }
                    }
                    else
                    {
                        foreach (Elemento propDesc in propiedadesDesconocidas)
                        {
                            listaDisjuntos.Add(getPropiedadObjeto(ontologia, propDesc));
                        }
                    }

                    // Vinculamos las clases disjuntas. Todas con todas
                    foreach (Propiedad disjunto in listaDisjuntos)
                    {
                        foreach (Propiedad disjuntoA in listaDisjuntos)
                        {
                            if (!disjunto.Equals(disjuntoA))
                            {
                                disjunto.DisjuntasPropiedades.Add(disjuntoA);
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Construye las propiedades de las clases del OWL
        /// </summary>
        /// <param name="ontologia">Ontología</param>
        /// <param name="root">Raiz del documento OWL</param>
        private static void construirPropiedadesObjetos(Ontologia ontologia, XElement root)
        {
            if (UtilidadesEspaciosNombres.contieneOWL(ontologia.getEspaciosNombresDictiorary()) && UtilidadesEspaciosNombres.contieneRDF(ontologia.getEspaciosNombresDictiorary()) && UtilidadesEspaciosNombres.contieneRDFS(ontologia.getEspaciosNombresDictiorary()))
            {
                Elemento elemento;
                PropiedadObjeto propiedad;
                Elemento about;

                // Obtenemos las listas de propiedades que vamos a manejar
                IList<Propiedad> propiedades = ontologia.PropiedadesObjetos;
                
                // Extraemos los elementos de las propiedades
                IEnumerable<XElement> elePropiedades =
                    from el in root.Elements(UtilidadesEspaciosNombres.getEspacioNombres(ontologia.getEspaciosNombresDictiorary()[EtiquetasOWL.NOMBRE]) + EtiquetasOWL.PROPIEDAD_OBJETO)
                    select el;

                // Creamos los objetos Clase a partir de los elementos recuperados
                foreach (XElement ele in elePropiedades)
                {
                    elemento = UtilidadesElementos.getElemento(UtilidadesElementos.getId(ele, ontologia.getEspaciosNombresDictiorary()), ontologia);
                    propiedad = new PropiedadObjeto(elemento);

                    // Extraemos las propiedades
                    extraerPropiedades(propiedad, ontologia, ele);

                    // Extraemos los dominios
                    extraerDominios(propiedad, ontologia, ele);

                    // Extraemos los rangos
                    extraerRangos(propiedad, ontologia, ele);

                    // La relacionesmos con la propiedad top
                    relacionarPropiedadConPropiedadObjetoTop(ontologia, ontologia.getEspaciosNombresDictiorary(), propiedad);

                    propiedades.Add(propiedad);
                }

                // Extraemos los elementos de las propiedades que tengan subpropiedades, equivalentes, disjuntas o inversas
                elePropiedades =
                    from el in root.Elements(UtilidadesEspaciosNombres.getEspacioNombres(ontologia.getEspaciosNombresDictiorary()[EtiquetasOWL.NOMBRE]) + EtiquetasOWL.PROPIEDAD_OBJETO)
                    where el.Elements(UtilidadesEspaciosNombres.getEspacioNombres(ontologia.getEspaciosNombresDictiorary()[EtiquetasRDFS.NOMBRE]) + EtiquetasRDFS.SUBPROPIEDAD).Count() > 0
                        || el.Elements(UtilidadesEspaciosNombres.getEspacioNombres(ontologia.getEspaciosNombresDictiorary()[EtiquetasOWL.NOMBRE]) + EtiquetasOWL.PROPIEDAD_EQUIVALENTE).Count() > 0
                        || el.Elements(UtilidadesEspaciosNombres.getEspacioNombres(ontologia.getEspaciosNombresDictiorary()[EtiquetasOWL.NOMBRE]) + EtiquetasOWL.DISJUNTA_CON_PROPIEDAD).Count() > 0
                        || el.Elements(UtilidadesEspaciosNombres.getEspacioNombres(ontologia.getEspaciosNombresDictiorary()[EtiquetasOWL.NOMBRE]) + EtiquetasOWL.PROPIEDAD_INVERSA).Count() > 0
                    select el;

                foreach (XElement elementoPropiedad in elePropiedades)
                {
                    about = UtilidadesElementos.getElemento(UtilidadesElementos.getId(elementoPropiedad, ontologia.getEspaciosNombresDictiorary()), ontologia);

                    // Extraemos las propiedades inversas
                    extraerPropiedadesInversas(about, ontologia, elementoPropiedad);

                    // Comprobamos si son sub propiedades de alguna propiedad
                    extraerSupPropiedades(about, ontologia, elementoPropiedad, false);

                    // Comprobamos si es equivalente a alguna propiedad con otra
                    extraerEquivalentesPropiedades(about, ontologia, elementoPropiedad, false);

                    // Comprobamos si es disjunta a alguna propiedad con otra
                    extraerDisjuntasPropiedades(about, ontologia, elementoPropiedad, false);
                }
            }
        }

        /// <summary>
        /// Extrae propiedades inversas de alguna propiedad
        /// </summary>
        /// <param name="about">Elemento que hemos identificado</param>
        /// <param name="ontologia">Ontolología a la que pertenece</param>
        /// <param name="dicPropiedades">Diccionario de propiedades</param>
        /// <param name="elementoPropiedad">Elemento que representa a la propiedad que estamos analizando</param>
        private static void extraerPropiedadesInversas(Elemento about, Ontologia ontologia, XElement elementoPropiedad)
        {
            Elemento recurso;

            IEnumerable<XElement> eleInvPropiedad =
                        from el in elementoPropiedad.Elements(UtilidadesEspaciosNombres.getEspacioNombres(ontologia.getEspaciosNombresDictiorary()[EtiquetasOWL.NOMBRE]) + EtiquetasOWL.PROPIEDAD_INVERSA)
                        select el;

            foreach (XElement eleSub in eleInvPropiedad)
            {
                recurso = UtilidadesElementos.getElemento(UtilidadesElementos.getNombreRecurso(eleSub, ontologia.getEspaciosNombresDictiorary()), ontologia);
                UtilidadesPropiedades.getPropiedadObjeto(ontologia, about).InversosPropiedadesObjeto.Add(UtilidadesPropiedades.getPropiedadObjeto(ontologia, recurso));
            }
        }

        /// <summary>
        /// Extrae los dominios de la propiedad
        /// </summary>
        /// <param name="propiedad">Propiedad a los que se le van añadir los rangos</param>
        /// <param name="ontologia">Ontologia base</param>
        /// <param name="elemento">Elemento OWL que estamos analizando</param>
        private static void extraerDominios(Propiedad propiedad, Ontologia ontologia, XElement elemento)
        {
            Elemento recurso;
            IEnumerable<XElement> eleDominios =
                        from el in elemento.Elements(UtilidadesEspaciosNombres.getEspacioNombres(ontologia.getEspaciosNombresDictiorary()[EtiquetasRDFS.NOMBRE]) + EtiquetasRDFS.DOMINIO)
                        select el;
            foreach (XElement elem in eleDominios)
            {
                recurso = UtilidadesElementos.getElemento(UtilidadesElementos.getNombreRecurso(elem, ontologia.getEspaciosNombresDictiorary()), ontologia);
                if (null != recurso) // Puede que sea una restriccion
                {
                    propiedad.Dominios.Add(UtilidadesClases.getClase(ontologia, recurso));
                }
            }
        }

        /// <summary>
        /// Extrae los rangos de la propiedad
        /// </summary>
        /// <param name="propiedad">Propiedad a los que se le van añadir los rangos</param>
        /// <param name="ontologia">Ontologia base</param>
        /// <param name="elemento">Elemento OWL que estamos analizando</param>
        private static void extraerRangos(Propiedad propiedad, Ontologia ontologia, XElement elemento)
        {
            Elemento recurso;

            IEnumerable<XElement> eleRangos =
                        from el in elemento.Elements(UtilidadesEspaciosNombres.getEspacioNombres(ontologia.getEspaciosNombresDictiorary()[EtiquetasRDFS.NOMBRE]) + EtiquetasRDFS.RANGO)
                        select el;
            foreach (XElement elem in eleRangos)
            {
                recurso = UtilidadesElementos.getElemento(UtilidadesElementos.getNombreRecurso(elem, ontologia.getEspaciosNombresDictiorary()), ontologia);
                if (null != recurso) // Puede que sea una restriccion
                {
                   propiedad.Rangos.Add(UtilidadesClases.getClase(ontologia, recurso));
                }
            }
        }

        /// <summary>
        /// Extrae las propiedades de la propiedad
        /// </summary>
        /// <param name="propiedad">Propiedad a los que se le van añadir los rangos</param>
        /// <param name="ontologia">Ontologia base</param>
        /// <param name="elemento">Elemento OWL que estamos analizando</param>
        private static void extraerPropiedades(Propiedad propiedad, Ontologia ontologia, XElement elemento)
        {
            Elemento tipoPopiedad;

            IEnumerable<XElement> eleTiposPropiedades =
                from el in elemento.Elements(UtilidadesEspaciosNombres.getEspacioNombres(ontologia.getEspaciosNombresDictiorary()[EtiquetasRDF.NOMBRE]) + EtiquetasRDF.TIPO)
                select el;
            foreach (XElement elem in eleTiposPropiedades)
            {
                tipoPopiedad = UtilidadesElementos.getElemento(UtilidadesElementos.getNombreRecurso(elem, ontologia.getEspaciosNombresDictiorary()), ontologia);
                if (ontologia.getEspaciosNombresDictiorary()[EtiquetasOWL.NOMBRE].Equals(tipoPopiedad.EspacioNombres))
                {
                    if (tipoPopiedad.Id == EtiquetasOWL.PROPIEDAD__FUNCIONAL)
                    {
                        propiedad.Tipos.Add(TipoPropiedad.FUNCIONAL);
                    }
                    else if (tipoPopiedad.Id == EtiquetasOWL.PROPIEDAD__TANSITIVA)
                    {
                        propiedad.Tipos.Add(TipoPropiedad.TRANSITIVA);
                    }
                    else if (tipoPopiedad.Id == EtiquetasOWL.PROPIEDAD__ASIMETRICA) // OWL 2
                    {
                        propiedad.Tipos.Add(TipoPropiedad.ASIMETRICA);
                    }
                    else if (tipoPopiedad.Id == EtiquetasOWL.PROPIEDAD__FUNCIONAL_INVERSA)
                    {
                        propiedad.Tipos.Add(TipoPropiedad.INVERSA_FUNCIONAL);
                    }
                    else if (tipoPopiedad.Id == EtiquetasOWL.PROPIEDAD__IRREFLEXIVA) // OWL 2
                    {
                        propiedad.Tipos.Add(TipoPropiedad.IRREFLEXIVA);
                    }
                    else if (tipoPopiedad.Id == EtiquetasOWL.PROPIEDAD__REFLEXIVA) // OWL 2
                    {
                        propiedad.Tipos.Add(TipoPropiedad.REFLEXIVA);
                    }
                    else if (tipoPopiedad.Id == EtiquetasOWL.PROPIEDAD__SIMETRICA)
                    {
                        propiedad.Tipos.Add(TipoPropiedad.SIMETRICA);
                    }
                }
            }
        }

        /// <summary>
        /// Obtiene la lista de propiedades a las que tengan en su dominio las clases que le pasemos
        /// </summary>
        /// <param name="propiedades">Lista de propiedades completas</param>
        /// <param name="clasesDominio">Lista de clases que tienen que pueden tener las propiedades en su dominio</param>
        /// <returns>Lista de propiedades filtrada</returns>
        public static List<Propiedad> obtenerPropiedades(IList<Propiedad> propiedades, IList<Clase> clasesDominio)
        {
            List<Propiedad> propiedadesFiltradas = new List<Propiedad>();

            foreach (Propiedad propiedad in propiedades)
            {
                foreach (Clase clase in clasesDominio)
                {
                    if ((propiedad.Dominios.Contains(clase) || propiedad.Dominios.Count == 0) && !propiedadesFiltradas.Contains(propiedad))
                    {
                        propiedadesFiltradas.Add(propiedad);
                    }
                }
            }

            return propiedadesFiltradas;
        }

        /// <summary>
        /// Devuelve la lista de propiedades de tipo Dato que contienen el individuo
        /// </summary>
        /// <param name="individuo">Individuo del que vamos a obetener los tipos de propiedades dato</param>
        /// <returns>Tipos de Propiedades Dato del individuo</returns>
        public static List<Propiedad> tiposPropiedadesDato(Individuo individuo)
        {
            List<Propiedad> propiedades = new List<Propiedad>();

            // Obtenemos las propiedades de Datos
            foreach (PropiedadDatoIndividuo propiedadDato in individuo.PropiedadesDatos)
            {
                if (!propiedades.Contains(propiedadDato.PropiedadDato))
                {
                    propiedades.Add(propiedadDato.PropiedadDato);
                }
            }

            return propiedades;
        }

        /// <summary>
        /// Devuelve la lista de propiedades de tipo Objeto que contienen el individuo
        /// </summary>
        /// <param name="individuo">Individuo del que vamos a obetener los tipos de propiedades objeto</param>
        /// <returns>Tipos de Propiedades Objeto del individuo</returns>
        public static List<PropiedadObjeto> tiposPropiedadesObjeto(Individuo individuo)
        {
            List<PropiedadObjeto> propiedades = new List<PropiedadObjeto>();

            // Obtenemos las propiedades de Objeto
            foreach (PropiedadObjetoIndividuo propiedadObjeto in individuo.PropiedadesObjetos)
            {
                if (!propiedades.Contains(propiedadObjeto.PropiedadObjeto))
                {
                    propiedades.Add(propiedadObjeto.PropiedadObjeto);
                }
            }

            return propiedades;
        }

        /// <summary>
        /// Compueba si existe las propiedades especiales como topDataProperty y topObjectProperty del OWL.
        /// </summary>
        /// <param name="ontologia">Ontología de entrada</param>
        /// <param name="espacioNombresDictionary">Espacios de nombres de la ontología</param>
        private static void crearPropiedadesEspeciales(Ontologia ontologia, IDictionary<string, EspacioNombres> espacioNombresDictionary)
        {
            crearPropiedadDatoTop(ontologia, espacioNombresDictionary);
            crearPropiedadObjetoTop(ontologia, espacioNombresDictionary);
        }

        /// <summary>
        /// Compueba si existe la propiedad topDataProperty y sino exite la añade.
        /// </summary>
        /// <param name="ontologia">Ontología de entrada</param>
        /// <param name="espacioNombresDictionary">Espacios de nombres de la ontología</param>
        private static void crearPropiedadDatoTop(Ontologia ontologia, IDictionary<string, EspacioNombres> espacioNombresDictionary)
        {
            Propiedad propiedad = new Propiedad(EtiquetasOWL.PROPIEDA_DATO_TOP, espacioNombresDictionary[EtiquetasOWL.NOMBRE]);
            if (!ontologia.PropiedadesDatos.Contains(propiedad))
            {
                ontologia.PropiedadesDatos.Add(propiedad);
            }
        }

        /// <summary>
        /// Compueba si existe la propiedad topDataProperty y sino exite la añade.
        /// </summary>
        /// <param name="ontologia">Ontología de entrada</param>
        /// <param name="espacioNombresDictionary">Espacios de nombres de la ontología</param>
        private static void crearPropiedadObjetoTop(Ontologia ontologia, IDictionary<string, EspacioNombres> espacioNombresDictionary)
        {
            PropiedadObjeto propiedad = new PropiedadObjeto(EtiquetasOWL.PROPIEDA_OBJETO_TOP, espacioNombresDictionary[EtiquetasOWL.NOMBRE]);
            if (!ontologia.PropiedadesObjetos.Contains(propiedad))
            {
                ontologia.PropiedadesObjetos.Add(propiedad);
            }
        }

        /// <summary>
        /// Compueba que todas las propiedades dato descienden de topDataProperty y si no es así las relaciona
        /// </summary>
        /// <param name="ontologia">Ontología de entrada</param>
        /// <param name="espacioNombresDictionary">Diccionario de espacio de nombres</param>
        /// <param name="propiedadHija">Propiedad hija</param>
        private static void relacionarPropiedadConPropiedadDatoTop(Ontologia ontologia, IDictionary<string, EspacioNombres> espacioNombresDictionary, Propiedad propiedadHija)
        {
            Propiedad propiedadTop = new Propiedad(EtiquetasOWL.PROPIEDA_DATO_TOP, espacioNombresDictionary[EtiquetasOWL.NOMBRE]);
            propiedadTop = ontologia.getPropiedadesDatosDictiorary()[propiedadTop.ToString()];

            if (!propiedadHija.PadrePropiedades.Contains(propiedadTop) && !propiedadHija.Equals(propiedadTop))
            {
                propiedadTop.SubPropiedades.Add(propiedadHija);
                propiedadHija.PadrePropiedades.Add(propiedadTop);
            }
        }

        /// <summary>
        /// Compueba que todas las propiedades objeto descienden de topDataProperty y si no es así las relaciona
        /// </summary>
        /// <param name="ontologia">Ontología de entrada</param>
        /// <param name="espacioNombresDictionary">Diccionario de espacio de nombres</param>
        /// <param name="propiedadHija">Propiedad hija</param>
        private static void relacionarPropiedadConPropiedadObjetoTop(Ontologia ontologia, IDictionary<string, EspacioNombres> espacioNombresDictionary, Propiedad propiedadHija)
        {
            Propiedad propiedadTop = new Propiedad(EtiquetasOWL.PROPIEDA_OBJETO_TOP, espacioNombresDictionary[EtiquetasOWL.NOMBRE]);
            propiedadTop = ontologia.getPropiedadesObjetosDictiorary()[propiedadTop.ToString()];

            if (!propiedadHija.PadrePropiedades.Contains(propiedadTop) && !propiedadHija.Equals(propiedadTop))
            {
                propiedadTop.SubPropiedades.Add(propiedadHija);
                propiedadHija.PadrePropiedades.Add(propiedadTop);
            }
        }

        /// <summary>
        /// Nos indica que la propiedad es una propiedad Top o no lo es.
        /// </summary>
        /// <param name="propiedad"></param>
        /// <param name="espacioNombresDictionary"></param>
        /// <returns></returns>
        public static bool isPropiedadTop(Propiedad propiedad, IDictionary<string, EspacioNombres> espacioNombresDictionary)
        {
            bool esTop = false;

            Propiedad propiedadTop = new Propiedad(EtiquetasOWL.PROPIEDA_DATO_TOP, espacioNombresDictionary[EtiquetasOWL.NOMBRE]);
            PropiedadObjeto propiedadTopObjeto = new PropiedadObjeto(EtiquetasOWL.PROPIEDA_OBJETO_TOP, espacioNombresDictionary[EtiquetasOWL.NOMBRE]);

            if (propiedad is PropiedadObjeto)
            {
                esTop = propiedadTopObjeto.Equals(propiedad);
            }
            else
            {
                esTop = propiedadTop.Equals(propiedad);
            }

            return esTop;
        }

        /// <summary>
        /// Nos devuelve la propiedad de tipo dato a la que corresponde el recurso. Si no existe la propiedad la agregamos
        /// </summary>
        /// <param name="ontologia">Ontología que estamos manejando</param>
        /// <param name="recurso">Recurso que representa la propiedad</param>
        /// <returns>Propiedad que representa el recurso</returns>
        public static Propiedad getPropiedadDato(Ontologia ontologia, Elemento recurso)
        {
            IDictionary<string, Propiedad> dicPropiedadDatos = ontologia.getPropiedadesDatosDictiorary();
            Propiedad propiedad = null;

            if (dicPropiedadDatos.ContainsKey(recurso.ToString()))
            {
                propiedad = dicPropiedadDatos[recurso.ToString()];
            }
            else
            {
                propiedad = new Propiedad(recurso);
                ontologia.PropiedadesDatos.Add(propiedad);
            }

            return propiedad;
        }

        /// <summary>
        /// Nos devuelve la propiedad de tipo dato a la que corresponde el recurso. Si no existe la propiedad la agregamos
        /// </summary>
        /// <param name="ontologia">Ontología que estamos manejando</param>
        /// <param name="recurso">Recurso que representa la propiedad</param>
        /// <returns>Propiedad que representa el recurso</returns>
        public static PropiedadObjeto getPropiedadObjeto(Ontologia ontologia, Elemento recurso)
        {
            IDictionary<string, Propiedad> dicPropiedadObjetos = ontologia.getPropiedadesObjetosDictiorary();
            PropiedadObjeto propiedad = null;

            if (dicPropiedadObjetos.ContainsKey(recurso.ToString()))
            {
                propiedad = dicPropiedadObjetos[recurso.ToString()] as PropiedadObjeto;
            }
            else
            {
                propiedad = new PropiedadObjeto(recurso);
                ontologia.PropiedadesObjetos.Add(propiedad);
            }

            return propiedad;
        }

        /// <summary>
        /// Nos devuelve la propiedad a la que corresponde el recurso. Si no existe la propiedad la agregamos
        /// </summary>
        /// <param name="ontologia">Ontología que estamos manejando</param>
        /// <param name="elemento">Elemento que representa la propiedad</param>
        /// <param name="isPropiedadDato">indica si estamos recuperando una propiedad Dato o de Objeto</param>
        /// <returns>Propiedad que representa el recurso</returns>
        public static Propiedad getPropiedad(Ontologia ontologia, Elemento elemento, bool isPropiedadDato)
        {
            Propiedad propiedad = null;

            if (isPropiedadDato)
            {
                propiedad = getPropiedadDato(ontologia, elemento);
            }
            else
            {
                propiedad = getPropiedadObjeto(ontologia, elemento);
            }

            return propiedad;
        }
    }
}
